// 
//  Xephyr.cs
//  
//  Author:
//       Nikita Tsukanov <keks9n@gmail.com>
// 
//  Copyright (c) 2011 Nikita Tsukanov
// 
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//  
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// 





using System;
namespace SkypeTab
{
	
	
	[System.ComponentModel.ToolboxItem(true)]
	public partial class Xephyr : Gtk.Bin
	{
		[System.Runtime.InteropServices.DllImport("libgtk-x11-2.0.so.0")]
		private static extern IntPtr gtk_socket_get_plug_window(IntPtr Socket);
		public X11.X X11 {get; private set;}
		
		
		public event EventHandler OnStarted;
		
		private string m_Display=":11";
		public string DisplayName {
			get
			{
				return m_Display;
			}
			set
			{
				if(m_Xephyr!=null) 
					throw new InvalidOperationException();
				m_Display=value;
			}
		}
		Gtk.Socket Socket=new Gtk.Socket();
		Gtk.DrawingArea DArea=new Gtk.DrawingArea();
		System.Diagnostics.Process m_Xephyr;
		
		Gdk.Window m_XephyrWindow;
		
		
		public int DisplayWidth
		{
			get
			{
				return (Allocation.Width|0x7)-8;
			}
		}
		public int DisplayHeight
		{
			get
			{
				return Allocation.Height;
			}
		}
		
		public Xephyr ()
		{
			
			Add(Socket);
			Socket.PlugAdded+= HandleSocketPlugAdded;
			ShowAll();
		}
		
		void FixKeyboard()
		{
			//Get keyboard settings from the main X Server
			string kbdata=ProcessUtil.ExecuteRead("xmodmap", "-pke");
			
			var kbmapfile=System.IO.Path.GetTempFileName();
			System.IO.File.WriteAllText(kbmapfile,kbdata);
			var p=ProcessUtil.Start ("xmodmap", kbmapfile,false,false,false,m_Display);
			p.WaitForExit();
			System.IO.File.Delete(kbmapfile);
		}

		void HandleSocketPlugAdded (object sender, EventArgs e)
		{
			m_XephyrWindow=new Gdk.Window(gtk_socket_get_plug_window(Socket.Handle));
			Console.WriteLine("Obtained Xephyr window handle");
			X11=new X11.X();
			for(uint i=0; i<20; i++)
			{
				if(X11.OpenDisplay(m_Display))
				{
					Console.WriteLine("Connected to Xephyr display, applying keyboard fix");
					FixKeyboard();
					
					if(OnStarted!=null)
						OnStarted(this, null);
					return;
				}
				System.Threading.Thread.Sleep(100);	
			}
			throw new Exception("Cann't connect to Xephyr display at "+m_Display);
		}
		
		~Xephyr()
		{
			Dispose();
		}
		
		private void ResizeXephyr(int w, int h)
		{
			//w=(w|0x7)-8;
			string mline=ProcessUtil.ExecuteRead("cvt", w+" "+h+" 60");

			mline=mline.Split('\n')[1];
			if(!mline.StartsWith("Modeline "))
			{
				throw new Exception ("Unable to get modeline!");
			}
			string mname=System.Text.RegularExpressions.Regex.Match(mline,
							"\".*\"").Value;
			mline=mline.Substring(9);
			ProcessUtil.Start("xrandr", "--newmode "+mline,false,false,false,m_Display).WaitForExit();
			ProcessUtil.Start("xrandr", "--addmode default "+mname,false,false,false,m_Display).WaitForExit();
			ProcessUtil.Start("xrandr", "-s "+mname,false,false,false,m_Display).WaitForExit();
			
			
		}
		
		protected override void OnSizeAllocated (Gdk.Rectangle allocation)
		{
			
			base.OnSizeAllocated (allocation);
			int orwidth=allocation.Width;
			int neww=orwidth;
			if((orwidth&0x7)!=0)
				allocation.Width=(allocation.Width|0x7)-8;
			
			Socket.SizeAllocate(allocation);
			if(orwidth==neww)
				DArea.Hide();
			else
			{
				DArea.Show();
				DArea.SizeAllocate(new Gdk.Rectangle(allocation.Width,0,orwidth-allocation.Width,allocation.Height));
			}
			if(m_XephyrWindow!=null)
			{
				m_XephyrWindow.Resize(allocation.Width,allocation.Height);
				ResizeXephyr(allocation.Width, allocation.Height);
			}
			
			if(!this.IsRealized)
				return;
			if(!this.Visible)
				return;
			Console.WriteLine("Size: {0}x{1}", allocation.Width,allocation.Height);
			if((allocation.Width>2) && (allocation.Height>2)&&(m_Xephyr==null))
			{
				
				Start();
			}
		}
		public Gtk.Widget FocusTarget
		{
			get
			{
				return Socket;
			}
		}
		
		private void Start()
		{

			
			
			Realize();
			Socket.Realize();
			m_Xephyr=ProcessUtil.Start("Xephyr",
			                           m_Display+" -parent "+Socket.Id+" -dpi 96",
										false,false,false,null);
			
			
			if(m_Xephyr==null)
				throw new Exception("Cann't start Xephyr!");
			
			
		}
		
		public override void Dispose ()
		{
			base.Dispose();
			if(m_Xephyr!=null)
			{
				try
				{
					m_Xephyr.Kill();
				}
				catch{};
				m_Xephyr.Dispose();
				m_Xephyr=null;
			}
		}
		
	}
}
